package com.letv.core.network.volley;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;

import com.letv.core.bean.DataHull;
import com.letv.core.bean.DataHull.DataType;
import com.letv.core.bean.LetvBaseBean;
import com.letv.core.bean.TimestampBean;
import com.letv.core.bean.UploadFileBean;
import com.letv.core.db.PreferencesManager;
import com.letv.core.network.volley.VolleyResponse.CacheResponseState;
import com.letv.core.network.volley.VolleyResponse.NetworkResponseState;
import com.letv.core.network.volley.VolleyResponse.ResponseSupplier;
import com.letv.core.network.volley.exception.DataIsErrException;
import com.letv.core.network.volley.exception.DataIsNullException;
import com.letv.core.network.volley.exception.DataNoUpdateException;
import com.letv.core.network.volley.exception.JsonCanNotParseException;
import com.letv.core.network.volley.exception.ParseException;
import com.letv.core.network.volley.exception.TokenLoseException;
import com.letv.core.network.volley.listener.OnEntryResponse;
import com.letv.core.network.volley.listener.OnPreExecuteListener;
import com.letv.core.network.volley.listener.VolleyCache;
import com.letv.core.network.volley.toolbox.ParameterBuilder;
import com.letv.core.network.volley.toolbox.VolleyDiskCache;
import com.letv.core.parser.LetvBaseParser;
import com.letv.core.parser.LetvMobileParser;
import com.letv.core.utils.EncryptUtils;
import com.letv.core.utils.LogInfo;

import org.apache.http.protocol.HTTP;

import java.util.HashMap;
import java.util.Map;

/**
 * 所有网络请求基类
 * 
 * @author zhuqiao
 * 
 */
public abstract class VolleyRequest<T extends LetvBaseBean> implements Comparable<VolleyRequest<T>> {
    private static final String DEFAULT_PARAMS_ENCODING = HTTP.UTF_8;
    /**
     * header:用户ssotk
     */
    private static final String SSOTK = "SSOTK";
    /**
     * header:请求加密串，所有请求均会验证其合法性.
     * <p>
     * TK生成方法：调用提供的加密包，传入时间戳和完整的请求地址，其返回值即为TK <br>
     * 如：TK = letv_encrypt(时间戳，完整的请求地址)
     */
    private static final String TK = "TK";

    private static final int DEFAULT_TIMEOUT = 20 * 1000;

    /**
     * http请求方式
     * 
     * @author zhuqiao
     */
    public enum HttpRequestMethod {
        /**
         * 根据body自动判断
         */
        AUTO,
        /**
         * get请求
         */
        GET,
        /**
         * post请求
         */
        POST
    }

    /**
     * 内部请求方式
     * 
     * @author zhuqiao
     */
    public enum RequestManner {
        /**
         * 先取缓存，再取线上数据
         */
        CACHE_THEN_NETROWK,
        /**
         * 先取线上数据，再取缓存
         */
        NETWORK_THEN_CACHE,
        /**
         * 只获取线上数据
         */
        NETWORK_ONLY,
        /**
         * 只获取缓存数据
         */
        CACHE_ONLY,
        /**
         * 先取缓存，如果缓存获取失败，那么再取服务器数据
         */
        CACHE_FAIL_THEN_NETWORK
    }

    /**
     * 请求优先级.
     * 
     * @author zhuqiao
     */
    public enum RequestPriority {
        LOW, NORMAL, HIGH, IMMEDIATE
    }

    public Context mContext;

    /** 封装完的缓存bean对象 **/
    protected T mCacheEntry;
    /** 封装完的网络bean对象 **/
    protected T mNetworkEntry;

    /** 接口地址 **/
    private String mUrl = "";

    /** 数据解析器 **/
    protected LetvBaseParser<T, ?> mParser;

    /** 请求方式 **/
    public RequestManner mRequestType = RequestManner.CACHE_THEN_NETROWK;

    /** 封装完成的dataHull(只跟network有关) **/
    public final DataHull mNetWorkDataHull = new DataHull();
    /** 封装完成的dataHull(只跟缓存有关) **/
    public final DataHull mCacheDataHull = new DataHull();

    /** http请求方式 **/
    public HttpRequestMethod mHttpRequestMethod = HttpRequestMethod.AUTO;

    /** 请求的序列号，用来执行FIFO **/
    private Integer mSequence;

    /** 请求队列 **/
    private VolleyRequestQueue mRequestQueue;

    /** post参数. **/
    private Map<String, String> mPostParams = new HashMap<String, String>();

    /** 文件post参数 **/
    private UploadFileBean[] mFilePostParams;

    /** 头信息 **/
    public Map<String, String> mHeadMap = new HashMap<String, String>();

    /** 请求是否被取消了 **/
    private boolean mCanceled = false;

    /** 请求前验证是否成功 **/
    private boolean mValidateSuccess = true;

    /** 用来标记请求 **/
    private String mTag;

    /** 封装完entry对象之后回调接口 **/
    protected OnEntryResponse<T> mEntryResponse;

    /** 重试策略 **/
    private final RetryPolicy mRetryPolicy = new RetryPolicy();

    /** 优先级 **/
    private RequestPriority mPriority = RequestPriority.NORMAL;

    /** 请求超时 **/
    public int mTimeOut = DEFAULT_TIMEOUT;

    /** 缓存方式 **/
    private VolleyCache<?> mVolleyCache;

    /** 请求执行前准备的回调接口 **/
    private OnPreExecuteListener mOnPreExecuteListener;

    /** 是否需要上报错误码 **/
    private boolean mPostErrorReport = false;

    /** 获取缓存是否成功 **/
    private boolean mCacheSuccess = false;

    /** 是否总是回调网络响应 **/
    private boolean mAlwaysCallbackNetworkResponse = false;

    /** 是否允许post值为空 **/
    private boolean mEnablePostEmpty = false;

    public VolleyRequest(Context context) {
        mContext = context.getApplicationContext();

        // 默认磁盘缓存
        mVolleyCache = new VolleyDiskCache(mContext);

        // 添加默认头信息
        mHeadMap.put(SSOTK, PreferencesManager.getInstance().getSso_tk());
    }

    // ========================= set =========================

    /**
     * 设置回调接口
     * 
     * @param response
     * @return
     */
    public final VolleyRequest<T> setCallback(OnEntryResponse<T> response) {
        mEntryResponse = response;
        return this;
    }

    /**
     * 设置接口地址
     * 
     * @param url
     * @return
     */
    public final VolleyRequest<T> setUrl(String url) {
        mUrl = url;
        if (!TextUtils.isEmpty(url) && url.contains("api.mob.app.letv.com")) {// 媒资接口需要加上TK校验
            String tk = EncryptUtils.letvEncrypt(TimestampBean.getTm().getCurServerTime() * 1L, url);
            mHeadMap.put(TK, tk);
        }
        LogInfo.log("volley", url);
        return this;
    }

    /**
     * 设置解析器
     * 
     * @param parser
     * @return
     */
    public final VolleyRequest<T> setParser(LetvBaseParser<T, ?> parser) {
        mParser = parser;
        return this;
    }

    /**
     * 添加post参数
     * 
     * @param key
     * @param value
     * @return
     */
    public final VolleyRequest<T> addPostParam(String key, String value) {
        mPostParams.put(key, value);
        return this;
    }

    /**
     * 添加post参数
     * 
     * @param postParams
     * @return
     */
    public final VolleyRequest<T> addPostParams(Map<String, String> postParams) {
        mPostParams.putAll(postParams);
        return this;
    }

    /**
     * 添加文件post参数
     * 
     * @param key
     * @param value 文件本地路径(sd卡中绝对路径)
     * @return
     */
    public final VolleyRequest<T> setFilePostParam(UploadFileBean[] formFiles) {
        mFilePostParams = formFiles;
        return this;
    }

    // /**
    // * 添加文件post参数
    // *
    // * @param filePostParams
    // * @return
    // */
    // public final VolleyRequest<T> addFilePostParams(Map<String, String> filePostParams) {
    // mFilePostParams.putAll(filePostParams);
    // return this;
    // }

    /**
     * 添加头信息
     * 
     * @param key
     * @param value
     * @return
     */
    public final VolleyRequest<T> addHead(String key, String value) {
        mHeadMap.put(key, value);
        return this;
    }

    /**
     * 添加头信息
     * 
     * @param key
     * @param value
     * @return
     */
    public final VolleyRequest<T> addHeads(Map<String, String> map) {
        mHeadMap.putAll(map);
        return this;
    }

    /**
     * 设置请求方式
     * 
     * @param method
     * @return
     */
    public final VolleyRequest<T> setHttpMethod(HttpRequestMethod method) {
        mHttpRequestMethod = method;
        return this;
    }

    /**
     * 设置请求的数据来源顺序
     * 
     * @param type
     */
    public final VolleyRequest<T> setRequestType(RequestManner type) {
        mRequestType = type;
        return this;
    }

    /**
     * 设置tag
     * 
     * @param tag
     * @return
     */
    public final VolleyRequest<T> setTag(String tag) {
        mTag = tag;
        return this;
    }

    /**
     * 设置重试策略
     * 
     * @param retryCount
     * @return
     */
    public final VolleyRequest<T> setMaxRetries(int maxNumRetries) {
        mRetryPolicy.setMaxRetries(maxNumRetries);
        return this;
    }

    /**
     * 设置请求优先级
     * 
     * @param priority
     * @return
     */
    public final VolleyRequest<T> setPriority(RequestPriority priority) {
        mPriority = priority;
        return this;
    }

    /**
     * 设置请求超时时间
     * 
     * @param timeOut
     * @return
     */
    public final VolleyRequest<T> setTimeOut(int timeOut) {
        mTimeOut = timeOut;
        return this;
    }

    /**
     * 设置缓存方式
     * 
     * @param cacheKey
     * @return
     */
    public final VolleyRequest<T> setCache(VolleyCache<?> volleyCache) {
        mVolleyCache = volleyCache;
        return this;
    }

    /**
     * 设置请求执行前准备的回调接口
     * 
     * @param listener
     * @return
     */
    public final VolleyRequest<T> setOnPreExecuteListener(OnPreExecuteListener listener) {
        mOnPreExecuteListener = listener;
        return this;
    }

    /**
     * 设置是否总是回调网络响应
     * </br>
     * 默认当缓存请求成功，网络请求异常时，不回调网络响应.
     * 
     * @param alwaysCallbackNetworkResponse 当设为true时，并且请求没有被cancel时，只要有网络请求，永远会响应网络回调
     * @return
     */
    public final VolleyRequest<T> setAlwaysCallbackNetworkResponse(boolean alwaysCallbackNetworkResponse) {
        mAlwaysCallbackNetworkResponse = alwaysCallbackNetworkResponse;
        return this;
    }

    /**
     * 允许post值为空
     * 
     * @param alwaysCallbackNetworkResponse
     * @return
     */
    public final VolleyRequest<T> enablePostEmpty(boolean alwaysCallbackNetworkResponse) {
        mEnablePostEmpty = true;
        return this;
    }

    /**
     * 设置该请求的序列号
     * 
     * @param sequence
     * @return
     */
    final void setSequence(int sequence) {
        mSequence = sequence;
    }

    /**
     * 关联该请求与这个队列.当该请求完成后这个队列会收到通知.
     * 
     * @param requestQueue
     * @return
     */
    final void setRequestQueue(VolleyRequestQueue requestQueue) {
        mRequestQueue = requestQueue;
    }

    /**
     * 设置获取缓存成功
     */
    final void setCacheSuccess() {
        mCacheSuccess = true;
    }

    /**
     * 添加进请求队列
     * 
     * @return
     */
    public abstract VolleyRequest<T> add();

    /**
     * 同步请求数据,并且返回
     */
    public abstract VolleyResult<T> syncFetch();

    /**
     * 设置entry
     * 
     * @param entry
     */
    @SuppressWarnings("unchecked")
    void setCacheEntry(LetvBaseBean entry) {
        mCacheEntry = (T) entry;
    }

    // ========================= get =========================

    /**
     * 请求的接口地址
     * 
     * @return
     */
    public String getUrl() {
        return mUrl;
    }

    /**
     * 返回编码格式
     * 
     * @return
     */
    String getParamsEncoding() {
        return DEFAULT_PARAMS_ENCODING;
    }

    public RetryPolicy getRetryPolicy() {
        return mRetryPolicy;
    }

    /**
     * 返回POST请求体的类型是文本类型
     * 
     * @return
     */
    String getBodyContentType() {
        return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
    }

    /**
     * 返回POST请求体
     * 
     * @return
     */
    byte[] getPostBody() {
        return ParameterBuilder.getByteArrayParams(mPostParams);
    }

    /**
     * 返回POST请求体.封装成StringBody
     * 
     * @return
     */
    Map<String, String> getPostFileBody() {
        return mPostParams;
    }

    /**
     * 返回POST文件请求体
     * 
     * @return
     */
    UploadFileBean[] getFileBody() {
        return mFilePostParams;
    }

    public String getTag() {
        return mTag;
    }

    final int getSequence() {
        if (mSequence == null) {
            throw new IllegalStateException("getSequence called before setSequence");
        }
        return mSequence;
    }

    /**
     * 是否已取消
     * 
     * @return
     */
    boolean isCanceled() {
        if (mCanceled) {
            // request is canceled
            if (!TextUtils.isEmpty(mTag)) {
                LogInfo.log("volley", mTag + " is canceled");
            }
        }
        return mCanceled;
    }

    /**
     * 是否验证失败
     * 
     * @return
     */
    boolean isValidateSuccess() {
        return mValidateSuccess;
    }

    /**
     * 是否需要上报错误码
     * 
     * @return
     */
    boolean shouldPostErrorReport() {
        return mPostErrorReport;
    }

    /**
     * 获取缓存接口,默认为无缓存.
     * 
     * @return
     */
    VolleyCache<?> getVolleyCache() {
        return mVolleyCache;
    }

    /**
     * 返回请求的优先级
     * 
     * @return
     */
    RequestPriority getPriority() {
        return mPriority;
    }

    /**
     * 获取缓存是否成功
     * 
     * @return
     */
    public boolean isCacheSuccess() {
        return mCacheSuccess;
    }

    boolean isAlwaysCallbackNetworkResponse() {
        return mAlwaysCallbackNetworkResponse;
    }

    boolean enablePostEmpty() {
        return mEnablePostEmpty;
    }

    /**
     * 返回缓存entry实例
     * 
     * @return
     */
    public T getCacheEntry() {
        return mCacheEntry;
    }

    /**
     * 返回网络entry实例
     * 
     * @return
     */
    public T getNetworkEntry() {
        return mNetworkEntry;
    }

    // ========================= state method =========================

    /**
     * 发起请求前的验证.
     * 
     * @return
     */
    protected boolean onPreExecute() {
        if (mOnPreExecuteListener != null) {
            return mOnPreExecuteListener.onPreExecute();
        }
        return true;
    }

    // ========================= method =========================

    /**
     * 取消这个请求.最终不会产生回调给调用者
     */
    public void cancel() {
        mCanceled = true;
    }

    /**
     * 设置验证失败
     */
    void validateFail() {
        mValidateSuccess = false;
    }

    /**
     * 设置需要发送错误码
     */
    void setNeedPostErrorReport() {
        mPostErrorReport = true;
    }

    /**
     * 通知请求队列该请求已经结束(不管请求是成功还是失败)
     */
    void finish() {
        if (mRequestQueue != null) {
            mRequestQueue.finish(this);
        }
    }

    /**
     * 交给子类解析
     * 
     * @param response
     */
    protected abstract void parse(VolleyResponse response, ResponseSupplier supplier) throws JsonCanNotParseException,
            ParseException, DataIsNullException, DataIsErrException, DataNoUpdateException, TokenLoseException;

    // ========================= 调度器回调 =========================

    /**
     * 网络数据回调
     * 
     * @param entry 数据实例对象
     * @param responseState 响应状态
     */
    protected void deliverNetworkResponse(NetworkResponseState responseState) {
        mNetWorkDataHull.setReportErrorString(getErrorInfo());
        if (mEntryResponse != null && mContext != null) {
            mEntryResponse.onNetworkResponse(this, mNetworkEntry, mNetWorkDataHull, responseState);
        }

        if (mNetWorkDataHull.getDataType() == DataType.TOKEN_LOSE) {
            sendTokenLoseBroadcast();
        }
    }

    /**
     * 缓存数据回调
     * 
     * @param entry 数据实例对象
     * @param responseState 响应状态
     */
    protected void deliverCacheResponse(CacheResponseState responseState) {
        if (mEntryResponse != null) {
            if (responseState != CacheResponseState.SUCCESS)
                mCacheDataHull.setMarkId("");
            mEntryResponse.onCacheResponse(this, mCacheEntry, mCacheDataHull, responseState);
        }
    }

    /**
     * 回调错误码上报
     */
    protected void deliverErrorReport() {
        if (mEntryResponse != null) {
            mEntryResponse.onErrorReport(this, getErrorInfo());
        }
    }

    /**
     * 发送token丢失通知
     * 
     * @param request
     */
    private void sendTokenLoseBroadcast() {
        Intent intent = null;
        if (isContainsCredit(mUrl)) {// 积分url
            // dialog
            intent = new Intent("TokenLoseReceiver1");
        } else {
            // toast
            intent = new Intent("TokenLoseReceiver2");
        }

        if (mContext != null && intent != null) {
            mContext.sendBroadcast(intent);
        }
    }

    private boolean isContainsCredit(String url) {
        return url.contains("&ctl=credit")
                && (url.contains("&act=status") || url.contains("&act=add") || url.contains("&act=list")
                        || url.contains("&act=getactioninfo") || url.contains("&act=getActionProgress"));
    }

    // ========================= letv 业务逻辑 =========================

    /**
     * 得到错误信息id
     * 
     * @return
     */
    int getErrCode() {
        return mParser == null ? 0 : mParser.getErrCode();
    }

    /**
     * 得到服务器信息
     * 
     * @return
     */
    String getMessage() {
        return mParser == null ? "" : mParser.getMessage();
    }

    /**
     * 得到markid
     * 
     * @return
     */
    String getMarkId() {
        return mParser instanceof LetvMobileParser ? ((LetvMobileParser<?>) mParser).getMarkId() : "";
    }

    /**
     * 获取错误信息
     * 
     * @return
     */
    String getErrorInfo() {
        String[] errorInfo = mNetWorkDataHull.getErrorInfo();
        String url = errorInfo[0];
        String status = errorInfo[1];
        String ut = errorInfo[2];

        if (TextUtils.isEmpty(status) || status.equals("null")) {
            status = "-";
        }
        if (TextUtils.isEmpty(ut) || ut.equals("null")) {
            ut = "-";
        }

        if (mNetWorkDataHull.getErrMsg() != -1) {
            status = status + "_" + mNetWorkDataHull.getErrMsg();
        } else {
            status = status + "_" + "-";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("url=").append(url).append("&");
        sb.append("status=").append(status).append("&");
        sb.append("ut=").append(ut);
        return sb.toString();
    }

    @Override
    public int compareTo(VolleyRequest<T> other) {
        RequestPriority left = getPriority();
        RequestPriority right = other.getPriority();

        return left == right ? mSequence - other.mSequence : right.ordinal() - left.ordinal();
    }
}
